package org.atlantis.juc.lock;

import java.util.concurrent.atomic.AtomicReference;

/**
 * 简单的CLH锁(Craig, Landin, and Hagersten)。<br>
 * 
 * <pre>
 * 加锁步骤：
 * 1.调用lock方法时创建一个新Node，并将此Node状态设置为获取锁
 * 2.将新Node加入到CLH队列中（将尾节点tail替换为新Node，将上一个节点Node设置为原尾节点）
 * 3.如果上一个节点获取了锁或等待获取锁（locked=true），则自选等待，如果上一个节点释放了锁（locked=false）
 *   则直接获取锁
 * 释放步骤：
 * 1.将当前Node的状态设置为释放锁
 * 2.将当前Node设为上一个节点
 * </pre>
 * 
 * @author <a href="mailto:seenoevil.cn@gmail.com">李曦</a><br/>
 *         $Created at: 2012-2-15 下午1:54:33
 */
public class CLHLock implements Lock {

	// 尾节点
	private final AtomicReference<Node> tail;
	// 上一个节点
	private final ThreadLocal<Node> pre;
	// 当前节点
	private final ThreadLocal<Node> current;

	public CLHLock() {
		tail = new AtomicReference<Node>(new Node());
		current = new ThreadLocal<Node>() {
			@Override
			protected Node initialValue() {
				return new Node();
			}

		};
		pre = new ThreadLocal<Node>();
	}

	@Override
	public void lock() {
		Node currentNode = current.get();
		currentNode.locked = true;
		Node preNode = tail.getAndSet(currentNode);
		pre.set(preNode);
		while (preNode.locked) {
			// 自旋等待
		}
	}

	@Override
	public void unlock() {
		Node node = current.get();
		node.locked = false;
		current.set(pre.get());
	}

	private static class Node {
		// locked为true表示当前线程已获得或正在尝试获得锁，false表示当前线程已释放锁
		volatile boolean locked = false;
	}
}
